﻿using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using CC.Yacht.Utilities;

namespace CC.Yacht.Windows.Forms
{
    public class Firework
    {
        //TODO: Move to CC.Controls
        public Firework(Control parent)
        {
            int size = RandomHelper.Next(7, 12);
            int xNegative = RandomHelper.Next(0, 2);
            double xVelocity = RandomHelper.NextDouble(MinXVelocity, MaxXVelocity);

            Color = Color.FromArgb(RandomHelper.Next(0, 256), RandomHelper.Next(0, 256), RandomHelper.Next(0, 256));
            Opacity = RandomHelper.NextDouble(0.6, 0.75);
            Parent = parent;
            Size = new Size(size, size);


            _InitialXVelocity = (xNegative == 1 ? xVelocity * -1 : xVelocity);
            _InitialYVelocity = RandomHelper.NextDouble(MinYVelocity + (Gravity * 2), MaxYVelocity);

            _XVelocity = _InitialXVelocity;
            _YVelocity = _InitialYVelocity;
        }

        private const double Gravity = 1.5;
        private const double MaxXVelocity = 3;
        private const double MaxYVelocity = 14;
        private const double MinXVelocity = 1;
        private const double MinYVelocity = 7;
        private const double MaxOpacity = 0.8;
        private const int NumberOfCircles = 12;
        private const double OpacityDelta = 0.045;

        #region Private Fields
        private int _ExplosionStep;
        private double _InitialXVelocity;
        private double _InitialYVelocity;
        private bool _IsExploded;
        private double _XVelocity;
        private double _YVelocity;
        #endregion

        #region Public Properties
        public Color Color { get; set; }

        public int ExplodeHeight { get; set; }

        public float Height
        {
            get { return Size.Height; }
        }

        public bool IsVisible
        {
            get
            {
                bool returnValue = true;

                if (Opacity <= 0)
                {
                    returnValue = false;
                }
                else if (Parent == null)
                {
                    returnValue = false;
                }
                else if ((Location.X < 0) || ((Location.X + Width) > Parent.Width) || (Location.Y < 0) || (Location.Y + Height) > Parent.Height)
                {
                    returnValue = false;
                }

                return returnValue;
            }
        }

        public PointF Location { get; set; }

        public double Opacity { get; set; }

        public Control Parent { get; set; }

        public SizeF Size { get; set; }

        public float Width
        {
            get { return Size.Width; }
        }
        #endregion

        #region Private Methods
        private static Color GetOpaqueColor(Color baseColor, double opacity)
        {
            if (opacity > 1.0)
            {
                opacity = 1.0;
            }

            return Color.FromArgb((int)(255 * opacity), baseColor);
        }

        private static Color GetRandomSmokeColor()
        {
            Color returnValue;
            const int alpha = 225;
            int index = RandomHelper.Next(0, 8);

            switch (index)
            {
                case 0:
                    {
                        returnValue = Color.FromArgb(alpha, Color.Green);
                        break;
                    }
                case 1:
                    {
                        returnValue = Color.FromArgb(alpha, Color.DimGray);
                        break;
                    }
                case 2:
                    {
                        returnValue = Color.FromArgb(alpha, Color.LightYellow);
                        break;
                    }
                case 3:
                    {
                        returnValue = Color.FromArgb(alpha, Color.IndianRed);
                        break;
                    }
                case 4:
                    {
                        returnValue = Color.FromArgb(alpha, Color.Ivory);
                        break;
                    }
                case 5:
                    {
                        returnValue = Color.FromArgb(alpha, Color.Salmon);
                        break;
                    }
                case 6:
                    {
                        returnValue = Color.FromArgb(alpha, Color.LightGray);
                        break;
                    }
                default:
                    {
                        returnValue = Color.FromArgb(alpha, Color.WhiteSmoke);
                        break;
                    }
            }

            return returnValue;
        }
        #endregion

        #region Public Methods
        public void Render(Graphics g)
        {
            if (!_IsExploded)
            {
                float baseX = Location.X + Width / 2;
                float baseY = Location.Y + Height / 2;
                float correctedXVelocity = (float)(_XVelocity / 20);
                float correctedYVelocity = (float)(_YVelocity / 20);

                if (_XVelocity < 0)
                {
                    correctedXVelocity *= -1.0f;
                }

                for (int i = 1; i < 20; i++)
                {
                    float currentXVelocity = (correctedXVelocity * i) * 1.25f;
                    float currentYVelocity = (correctedYVelocity * i) * 2;

                    float negativeXVelocity = currentXVelocity;
                    float positiveXVelocity = currentXVelocity;

                    if (negativeXVelocity > 0.0)
                    {
                        negativeXVelocity *= -1;
                    }
                    else if (positiveXVelocity < 0.0)
                    {
                        positiveXVelocity *= -1;
                    }

                    for (int j = i; j >= 0; j--)
                    {
                        float currentX = baseX + (float)RandomHelper.NextDouble(negativeXVelocity, positiveXVelocity);
                        float currentY = baseY + currentYVelocity + (float)RandomHelper.NextDouble(currentYVelocity * -1, currentYVelocity);
                        RectangleF trailRectangle = new RectangleF(currentX, currentY, 1, 1);

                        using (SolidBrush solidBrush = new SolidBrush(GetRandomSmokeColor()))
                        {
                            g.FillEllipse(solidBrush, trailRectangle);
                        }
                    }
                }
            }
            else
            {
                for (int i = _ExplosionStep * 2; i >= 1; i--)
                {
                    RectangleF explosionRectangle = new RectangleF(Location.X - (i * 2), Location.Y - (i * 2), Width + (i * 4), Height + (i * 4));

                    //using (Pen pen = new Pen(GetOpaqueColor(GetRandomSmokeColor(), Opacity), 0.5f))
                    using (Pen pen = new Pen(GetOpaqueColor(i % 2 == 0 ? Color : GetRandomSmokeColor(), Opacity), 0.5f))
                    {
                        pen.DashStyle = DashStyle.Dot;
                        pen.DashCap = DashCap.Round;
                        g.DrawEllipse(pen, explosionRectangle);
                    }
                }

                _ExplosionStep++;
            }

            double opacityStep = Opacity / (NumberOfCircles * 2);
            float heightStep = Height / (NumberOfCircles + 1);
            float widthStep = Width / (NumberOfCircles + 1);
            double currentOpacity = opacityStep;
            RectangleF currentRectangle = new RectangleF(Location.X, Location.Y, Width, Height);

            for (int i = 0; i < NumberOfCircles; i++)
            {
                using (SolidBrush solidBrush = new SolidBrush((i == (NumberOfCircles - 1)) ? Color.FromArgb(225, Color.White) : GetOpaqueColor(Color, currentOpacity)))
                {
                    g.FillEllipse(solidBrush, currentRectangle);
                }

                currentOpacity += opacityStep;
                currentRectangle.Height -= heightStep;
                currentRectangle.Width -= widthStep;
                currentRectangle.X += widthStep / 2;
                currentRectangle.Y += heightStep / 2;
            }

        }

        public void Run()
        {
            if (!_IsExploded)
            {
                Opacity += OpacityDelta;

                if (Opacity > MaxOpacity)
                {
                    Opacity = MaxOpacity;
                }

                if ((Location.Y - ExplodeHeight) > 200)
                {
                    //_YVelocity += _Acceleration;
                }
                else
                {
                    //_YVelocity -= Gravity;
                }

                if (_YVelocity < 0)
                {
                    _YVelocity = MinYVelocity;
                }

                Location = new PointF(Location.X + (float)_XVelocity, Location.Y - (float)_YVelocity);

                if (Location.Y < ExplodeHeight)
                {
                    _IsExploded = true;
                }
            }
            else
            {
                double delta = Gravity / _YVelocity;
                Opacity -= OpacityDelta;

                if (Opacity < 0)
                {
                    Opacity = 0;
                }

                Location = new PointF(Location.X + (float)_XVelocity * (float)delta, Location.Y + (float)(_ExplosionStep > 5 ? Gravity : _YVelocity / _ExplosionStep * -1));
            }
        }
        #endregion

    }
}
